package com.mike.bdb.manager;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mike.bdb.util.DataCurrent;
import com.mike.bdb.util.Folder;
import com.mike.bdb.util.ReflectUtil;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.Transaction;
import com.sleepycat.persist.EntityCursor;
import com.sleepycat.persist.EntityJoin;
import com.sleepycat.persist.EntityStore;
import com.sleepycat.persist.ForwardCursor;
import com.sleepycat.persist.PrimaryIndex;
import com.sleepycat.persist.SecondaryIndex;
import com.sleepycat.persist.StoreConfig;

public class DBManager {
	
	private static final Logger LOG = LoggerFactory.getLogger(DBManager.class);
	
	public static final String BERKELEY_DATABASE_SECONDARY_INDEX_SPLIT_CHAR = "#";
	public static final String BERKELEY_DATABASE_DPL = "persist";
	public static final String BERKELEY_DATABASE_PREFIX = "#com.sleepycat.";
	public static final String BERKELEY_DATABASE_DEFAULT_FORMAT = BERKELEY_DATABASE_PREFIX + BERKELEY_DATABASE_DPL + ".formats";
	public static final String BERKELEY_DATABASE_DEFAULT_SEQUENCES = BERKELEY_DATABASE_PREFIX + BERKELEY_DATABASE_DPL + ".sequences";
	
	//TODO 初始数据库设置
	public static final String DEFAULT_DB_FOLDER = "dpl-db";
//	public static final String DEFAULT_DB_FOLDER = "../bdb-demo2/bdb-db";
	
	public static final String DEFAULT_STORE = "EntityStore";
	
	private volatile String home = DEFAULT_DB_FOLDER;
	private volatile String store = DEFAULT_STORE;
	
	public static final boolean READ_ONLY = false;

	public String getStore() {
		return store;
	}

	public String getHome() {
		return home;
	}

	private static DBManager instance = new DBManager();
	
	private Environment	environment;
	private EntityStore entityStore;
	
	public <PK> PrimaryIndex<PK, Object> getPKStore(Class<PK> pk, Class<Object> entityClass) {
		return entityStore.getPrimaryIndex(pk, entityClass);
	}
	
	public <SK, PK> SecondaryIndex<SK, PK, Object> getSKStore(Class<PK> pk, Class<Object> entityClass, Class<SK> keyClass, String keyName) {
		return entityStore.getSecondaryIndex(getPKStore(pk, entityClass), keyClass, keyName);
	}
	
	public ForwardCursor getAllFieldSKStore(
			Map<Method, Text> methodMap, HashMap<String, Type> methodReturnMap) {
		
		ForwardCursor result = null;
		
		String curStoreClazz = DataCurrent.getInstance().curStoreClazz;
		if(null == curStoreClazz) return result;
		
		Class<Object> classBy = ReflectUtil.getClassBy(curStoreClazz);
		if(null == classBy) return result;
		
		Class pkClass = ReflectUtil.getPKClass(classBy);
		
		Iterator<Entry<Method, Text>> iterator = methodMap.entrySet().iterator();
		
		PrimaryIndex pkStore = getPKStore(pkClass, classBy);
		
		EntityJoin<Class, Class<Object>> join = new EntityJoin<Class, Class<Object>> (pkStore);
		
		boolean isHas = false;
		
		while (iterator.hasNext()) {
			
			Entry<Method, Text> next = iterator.next();
			
			Method key = next.getKey();
			
			Text value = next.getValue();
			
			if(null == value || "".equals(value.getText().trim())) {
				continue;
			}
			
			String skName = key.getName(); 
			
			skName = skName.replace("get", "");
			
			skName = skName.substring(0, 1).toLowerCase() + skName.substring(1, skName.length());
			
			Class skClass = ReflectUtil.getFieldClass(classBy, skName);
			
			Class skClass2 = ReflectUtil.getSKClass(classBy, skName);
			if(null == skClass2) continue;
			
			SecondaryIndex secondaryIndex = entityStore.getSecondaryIndex(pkStore, skClass2, skName);
			
			Type type = methodReturnMap.get(key.getName());
			
			try {
				Object typeObject = ReflectUtil.getRealType(type.toString(), value.getText());
				join.addCondition(secondaryIndex, typeObject);
				isHas = true;
			} catch (Exception e) {
				//eat it.
			}
			
		}
		
		return isHas ? join.entities() : null;
	}
	
	public Object getAllFieldPKStore(
			Map<Method, Text> methodMap, HashMap<String, Type> methodReturnMap) {

		Object result = null;
		
		String curStoreClazz = DataCurrent.getInstance().curStoreClazz;
		if(null == curStoreClazz) return result;
		
		Class<Object> classBy = ReflectUtil.getClassBy(curStoreClazz);
		if(null == classBy) return result;
		
		Class pkClass = ReflectUtil.getPKClass(classBy);
		
		Iterator<Entry<Method, Text>> iterator = methodMap.entrySet().iterator();
		
		PrimaryIndex pkStore = getPKStore(pkClass, classBy);
		
		
		while (iterator.hasNext()) {
			
			Entry<Method, Text> next = iterator.next();
			
			Method key = next.getKey();
			
			Text value = next.getValue();
			
			if(null == value || "".equals(value.getText().trim())) {
				continue;
			}
			
			String skName = key.getName(); 
		
			skName = skName.replace("get", "");
			
			skName = skName.substring(0, 1).toLowerCase() + skName.substring(1, skName.length());
			
			Class skClass = ReflectUtil.getFieldClass(classBy, skName);
			
			Type type = methodReturnMap.get(key.getName());
			
			result = pkStore.get(ReflectUtil.getRealType(type.toString(), value.getText()));
		}
		
		return result;
	}
	
	public EntityStore getEntityStore() {
		return entityStore;
	}
	
	private volatile boolean isUseRep = true;
	
	public void notUseDPL() {
		isUseRep = false;
	}
	
	public void useDPL() {
		isUseRep = true;
	}
	
	/** Don't allow new it outside. */
	private  DBManager() {
		initEnv();
	}

	public boolean updateHomeAndStore(String home, String store) {
		if(null == home || "".equals(home.trim()) || this.home.equals(home.trim())) return false;
		if(null == store || "".equals(store.trim()) || this.store.equals(store.trim())) return false;
		this.home  = home;
		this.store = store;
		return reload();
	}
	
	public boolean updateHome(String home) {
		if(null == home || "".equals(home.trim()) || this.home.equals(home.trim())) return false;
		this.home = home;
		return reload();
	}
	
	public boolean updateStore(String store) {
		if(null == store || "".equals(store.trim()) || this.store.equals(store.trim())) return false;
		this.store = store;
		return reload();
	}
	
	private boolean reload() {
		release();
		return initEnv();
	}
	
	private boolean initEnv() {
		
		boolean initSuccessed = false;
		
		if(LOG.isDebugEnabled())
			LOG.debug("init berkeley db...");
		
		try {
			if (isUseRep) {
				initRep(home, READ_ONLY);
			} else {
				init(home);
			}
			initSuccessed = true;
		} catch (Exception e) {
			//ignore
		}
		
		if(LOG.isDebugEnabled()) 
			LOG.debug("init berkeley db finished.");
		
		return initSuccessed;
	}
	
	/**
	 * 环境初始化
	 */
	private void init(String home) {
		
		if(null == home || "".equals(home.trim())) {
			return;
		}
		
		EnvironmentConfig environmentConfig = new EnvironmentConfig();
		environmentConfig.setAllowCreate(true);
		
		File folder = Folder.getFolder(home);
		environment = new Environment(folder, environmentConfig);
		
		if (LOG.isDebugEnabled()) {
			LOG.debug("Defalut Environment Home:" + environment.getHome());
		}
		
		DataCurrent.getInstance().curDataBasePath = folder.getAbsolutePath();
	}
	
	private void initRep(String home, boolean readOnly) {
		
		EnvironmentConfig environmentConfig = new EnvironmentConfig();
		StoreConfig storeConfig = new StoreConfig();
		
		environmentConfig.setReadOnly(readOnly);
		storeConfig.setReadOnly(readOnly);
		
		environmentConfig.setAllowCreate(!readOnly);
		storeConfig.setAllowCreate(!readOnly);
		
		File folder = Folder.getFolder(home);
		
		environment = new Environment(folder, environmentConfig);
		entityStore = new EntityStore(environment, store, storeConfig);
		
		if (LOG.isDebugEnabled()) {
			LOG.debug("DPL Environment Home:" + environment.getHome());
			LOG.debug("DPL Store Name:" + entityStore.getStoreName());
		}
		
		DataCurrent.getInstance().curDataBasePath = folder.getAbsolutePath(); 
	}

	public static DBManager getInstance() {
		return instance;
	}
	
	public Database getDatabase(String databaseName) {
		return getDatabase(databaseName, null);
	}
	
	public Database getDatabase(String databaseName, Transaction txn) {
		if(null == databaseName || databaseName.trim().equals("")) return null;
		DatabaseConfig databaseConfig = new DatabaseConfig();
		databaseConfig.setAllowCreate(true);
		if(null == environment) return null;
		return environment.openDatabase(txn, databaseName, databaseConfig);
	}
	
	public void release() {
		if(LOG.isDebugEnabled())
			LOG.debug("reload berkeley db...");
		
		if(null != entityStore) entityStore.close();
		if(null != environment) environment.close();
		
		if(LOG.isDebugEnabled()) 
			LOG.debug("reload finished.");
	}
	
	public List<String> getDatabases() {
		if(null == environment) return null;
		List<String> databaseNames = environment.getDatabaseNames();
		return databaseNames;
	}

	
}
